/*
 * commons is a based project implemented
 * Copyright (C) 2024 Jasmine
 */
package com.easy.auth.satoken.autoconfigure;

import org.redisson.client.RedisClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.easy.auth.satoken.core.SaTokenDaoRedisImpl;
import com.easy.core.constant.StringPool;
import com.easy.core.utils.GeneralPropertySourceFactory;
import com.easy.redis.config.RedissonAutoConfiguration;

import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.annotation.PostConstruct;

/**
 * Sa-Token 自动配置
 *
 * @author Jasmine
 * @since 1.0.0
 */
@AutoConfiguration
@ComponentScan("com.easy.auth.satoken.exception")
@EnableConfigurationProperties(SaTokenExtensionProperties.class)
@ConditionalOnProperty(prefix = "sa-token.extension", name = "enabled", havingValue = "true")
@PropertySource(value = "classpath:default-auth-satoken.yml", factory = GeneralPropertySourceFactory.class)
public class SaTokenAutoConfiguration implements WebMvcConfigurer {

    private static final Logger log = LoggerFactory.getLogger(SaTokenAutoConfiguration.class);

    private final SaInterceptor saInterceptor;
    private final SaTokenExtensionProperties properties;

    public SaTokenAutoConfiguration(@Autowired(required = false) SaInterceptor saInterceptor,
        SaTokenExtensionProperties properties) {
        this.saInterceptor = saInterceptor;
        this.properties = properties;
    }

    /** 注册 Sa-Token 拦截器 */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        if (!properties.getPermission().isEnabled()) {
            SaInterceptor saInterceptor = new SaInterceptorConfig().saInterceptor(properties);
            registry.addInterceptor(saInterceptor).addPathPatterns(StringPool.PATH_PATTERN);
        } else {
            registry.addInterceptor(saInterceptor).addPathPatterns(StringPool.PATH_PATTERN);
        }
    }

    /** Sa-Token 拦截器 */
    static class SaInterceptorConfig {
        @Bean
        @ConditionalOnMissingBean
        public SaInterceptor saInterceptor(SaTokenExtensionProperties properties) {
            // 注册 Sa-Token 拦截器，校验规则为 StpUtil.checkLogin() 登录校验
            SaInterceptor saInterceptor = new SaInterceptor(handle -> {
                SaRouter.match(StringPool.PATH_PATTERN).notMatch(properties.getSecurity().getExcludes())
                    .check(r -> StpUtil.checkLogin());
                // 路由拦截鉴权
                // SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
                // 是否启用掉注解鉴权能力，关闭了，框架就只会做路由拦截校验。 true-启用，false-关闭。
            }).isAnnotation(properties.getPermission().isAnnotation());
            return saInterceptor;
        }
    }

    /**
     * 持久层配置 -Redis
     */
    @ConditionalOnClass(RedisClient.class)
    @ConditionalOnMissingBean(SaTokenDao.class)
    @AutoConfigureBefore(RedissonAutoConfiguration.class)
    @ConditionalOnProperty(name = "sa-token.extension.dao.redis", havingValue = "true")
    static class Redis {
        static {
            log.debug("[Common Starter] - Auto Configuration 'SaToken-SaTokenDao-Redis' completed initialization.");
        }

        @Bean
        public SaTokenDao saTokenDao() {
            return new SaTokenDaoRedisImpl();
        }
    }

    @PostConstruct
    public void postConstruct() {
        log.debug("[Common Starter] - Auto Configuration 'SaToken' completed initialization.");
    }
}
